텐서플로우 허브는 이미 학습된 모델의 구성 요소를 공유하는 방법입니다. 이번 튜토리얼을 통해서 다음을 알아보겠습니다.
tf.keras
로 텐서플로우 허브를 사용하는 방법import warnings
warnings.simplefilter('ignore')
import matplotlib.pylab as plt
import tensorflow as tf
import tensorflow_hub as hub
from tensorflow.keras import layers
hub.module
을 사용하여 모바일넷(mobilenet)을 불러오고 tf.keras.layers.Lambda
를 사용하여 이를 케라스 레이어로 감싸줍니다. tfhub.dev의 TensorFlow 2와 호환가능한 모든 이미지 분류기 URL은 여기에서 확인할 수 있습니다.
classifier_url ="https://tfhub.dev/google/tf2-preview/mobilenet_v2/classification/2" #@param {type:"string"}
IMAGE_SHAPE = (224, 224)
classifier = tf.keras.Sequential([
hub.KerasLayer(classifier_url, input_shape=IMAGE_SHAPE+(3,))
])
이미지를 하나 다운받아 모델을 실행해봅시다.
import numpy as np
import PIL.Image as Image
grace_hopper = tf.keras.utils.get_file('image.jpg','https://storage.googleapis.com/download.tensorflow.org/example_images/grace_hopper.jpg')
grace_hopper = Image.open(grace_hopper).resize(IMAGE_SHAPE)
grace_hopper
grace_hopper = np.array(grace_hopper)/255.0
grace_hopper.shape
배치의 차원 수(batch dimension)를 넣고 이미지를 모델에 전달합니다.
result = classifier.predict(grace_hopper[np.newaxis, ...])
result.shape
결과 값으로는 이미지에 대해 각 클래스의 확률을 계산한 1001개로 구성된 로짓(logit)의 요소 벡터(element vector)가 도출되고 이미지에 대한 각 클래스의 확률을 계산합니다.
최상위 클래스의 ID는 argmax로 확인할 수 있습니다
predicted_class = np.argmax(result[0], axis=-1)
predicted_class
우리는 이제 예측된 클래스 ID를 가지고 있습니다. ImageNet
의 레이블을 가져와 예측 값을 디코딩해봅시다.
labels_path = tf.keras.utils.get_file('ImageNetLabels.txt','https://storage.googleapis.com/download.tensorflow.org/data/ImageNetLabels.txt')
imagenet_labels = np.array(open(labels_path).read().splitlines())
plt.imshow(grace_hopper)
plt.axis('off')
predicted_class_name = imagenet_labels[predicted_class]
_ = plt.title("Prediction: " + predicted_class_name.title())
TF Hub를 사용하면 모델의 최상위 레이어을 재학습하여 데이터셋의 클래스를 쉽게 인식할 수 있습니다.
이번 튜토리얼에서는 텐서플로우의 꽃 데이터셋을 사용할 것입니다:
data_root = tf.keras.utils.get_file(
'flower_photos','https://storage.googleapis.com/download.tensorflow.org/example_images/flower_photos.tgz',
untar=True)
tf.keras.preprocessing.image.ImageDataGenerator
을 사용하면 쉽게 데이터를 모델에 불러올 수 있습니다.
TensorFlow Hub의 모든 이미지 모듈은 [0, 1]
범위 내의 float 형태의 입력 값을 기대합니다. ImageDataGenerator
의 rescale
매개 변수를 사용하여 이 작업을 수행합니다.
이미지 크기는 나중에 처리할 것입니다.
image_generator = tf.keras.preprocessing.image.ImageDataGenerator(rescale=1/255)
image_data = image_generator.flow_from_directory(str(data_root), target_size=IMAGE_SHAPE)
빠른 학습을 위해 이미지의 개수를 줄이겠습니다.
image_data.samples = 20
image_data.samples
결과 객체는 image_batch, label_batch
쌍을 반환하는 반복자(iterator)입니다.
for image_batch, label_batch in image_data:
print("Image batch shape: ", image_batch.shape)
print("Label batch shape: ", label_batch.shape)
break
이제 이미지 배치에 분류기를 실행해봅시다.
result_batch = classifier.predict(image_batch)
result_batch.shape
predicted_class_names = imagenet_labels[np.argmax(result_batch, axis=-1)]
predicted_class_names
이제 이러한 예측이 이미지와 얼마나 일치하는지 확인해봅시다.
plt.figure(figsize=(10,9))
plt.subplots_adjust(hspace=0.5)
for n in range(30):
plt.subplot(6,5,n+1)
plt.imshow(image_batch[n])
plt.title(predicted_class_names[n])
plt.axis('off')
_ = plt.suptitle("ImageNet predictions")
결과가 완벽하지는 않지만, 나온 결과들이 모델이 학습한 클래스가 아니라는 점을 고려하면 상당히 괜찮다고 볼 수 있습니다.(“Daisy” 제외).
또한 텐서플로우 허브는 최상위 분류 레이어 없이 모델을 배포합니다. 이는 전이 학습을 쉽게 만들어줍니다.
tfhub.dev의 텐서플로우 2와 호환가능한 이미지 피쳐 벡터 URL은 여기서 확인할 수 있습니다.
feature_extractor_url = "https://tfhub.dev/google/tf2-preview/mobilenet_v2/feature_vector/2" #@param {type:"string"}
피쳐 추출기(extractor)를 생성합니다.
feature_extractor_layer = hub.KerasLayer(feature_extractor_url,
input_shape=(224,224,3))
각 이미지에 대해 길이가 1280인 벡터를 반환합니다:
feature_batch = feature_extractor_layer(image_batch)
print(feature_batch.shape)
피쳐 추출기 레이어의 변수를 프리즈(freeze)하여 새로운 분류기 레이어에 한정(modify)하여 학습하도록 합니다.
feature_extractor_layer.trainable = False
tf.keras.Sequential
에 허브 레이어를 감싸고 새로운 분류 레이어를 추가합니다.
model = tf.keras.Sequential([
feature_extractor_layer,
layers.Dense(image_data.num_classes, activation='softmax')
])
model.summary()
predictions = model(image_batch)
predictions.shape
컴파일을 사용하여 교육 프로세스를 구성합니다:
model.compile(
optimizer=tf.keras.optimizers.Adam(),
loss='categorical_crossentropy',
metrics=['acc'])
.fit
메서드를 사용해서 모델을 학습시킵니다.
간단하게 학습하기 위해 에포크(epoch)는 3로 설정합니다. 맞춤형 콜백(callback)을 사용하여 에포크별 평균 대신 각 배치의 손실과 정확도를 기록해서 학습 진행 상황을 시각화합니다.
class CollectBatchStats(tf.keras.callbacks.Callback):
def __init__(self):
self.batch_losses = []
self.batch_acc = []
def on_train_batch_end(self, batch, logs=None):
self.batch_losses.append(logs['loss'])
self.batch_acc.append(logs['acc'])
self.model.reset_metrics()
steps_per_epoch = np.ceil(image_data.samples/image_data.batch_size)
batch_stats_callback = CollectBatchStats()
history = model.fit_generator(image_data, epochs=2,
steps_per_epoch=steps_per_epoch,
callbacks = [batch_stats_callback])
이제 학습을 몇 번만 반복해도, 모델이 작업에 진전되고 있다는 것을 알 수 있습니다.
plt.figure()
plt.ylabel("Loss")
plt.xlabel("Training Steps")
plt.ylim([0,2])
plt.plot(batch_stats_callback.batch_losses)
plt.figure()
plt.ylabel("Accuracy")
plt.xlabel("Training Steps")
plt.ylim([0,1])
plt.plot(batch_stats_callback.batch_acc)
이전의 그림을 다시 보려면 먼저 정리된 클래스 이름의 리스트를 가져옵니다:
class_names = sorted(image_data.class_indices.items(), key=lambda pair:pair[1])
class_names = np.array([key.title() for key, value in class_names])
class_names
모델을 통해 이미지 배치를 실행하고 인덱스를 클래스 이름으로 변환합니다.
predicted_batch = model.predict(image_batch)
predicted_id = np.argmax(predicted_batch, axis=-1)
predicted_label_batch = class_names[predicted_id]
결과를 봅시다.
label_id = np.argmax(label_batch, axis=-1)
plt.figure(figsize=(10,9))
plt.subplots_adjust(hspace=0.5)
for n in range(30):
plt.subplot(6,5,n+1)
plt.imshow(image_batch[n])
color = "green" if predicted_id[n] == label_id[n] else "red"
plt.title(predicted_label_batch[n].title(), color=color)
plt.axis('off')
_ = plt.suptitle("Model predictions (green: correct, red: incorrect)")
모델을 학습시켰으면 저장된 모델로 내보냅니다:
import time
t = time.time()
export_path = "/tmp/saved_models/{}".format(int(t))
model.save(export_path, save_format='tf')
export_path
이제 다시 불러올 수 있는지, 또 동일한 결과값을 내는지 확인합니다.
reloaded = tf.keras.models.load_model(export_path)
result_batch = model.predict(image_batch)
reloaded_result_batch = reloaded.predict(image_batch)
abs(reloaded_result_batch - result_batch).max()
저장된 모델은 나중에 인퍼런스(inference)를 하기 위해 나중에 불러오거나 TFLite나 TFjs로 변환할 수 있습니다.
#@title Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.